package com.mini.common.web.config;

import feign.RequestInterceptor;
import org.springframework.boot.web.servlet.ServletRegistrationBean;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.web.context.request.RequestContextHolder;
import org.springframework.web.context.request.ServletRequestAttributes;
import org.springframework.web.servlet.DispatcherServlet;

import javax.servlet.http.HttpServletRequest;
import java.util.Enumeration;

/**
 * 解决feign调用的一些问题
 * @author Zhou Feng
 */
@Configuration
public class FeignConfig {

    /**
     * 解决feign同步调用请求头丢失的问题，如果不需要使用到当前线程的request,则此问题可以不用考虑。
     * 原因：Feign 远程调用时会生成代理对象，实际上new了一个新的RequestTemplate，没有使用到当前线程的request，没有把之前请求的header给设置进去
     * 解决：覆写拦截器，在feign发送请求前取出原来的header并转发
     */
    @Bean
    RequestInterceptor requestInterceptor(){
        return requestTemplate -> {
            ServletRequestAttributes attributes = (ServletRequestAttributes) RequestContextHolder.getRequestAttributes();
            if (attributes!=null){
                HttpServletRequest request = attributes.getRequest();
                Enumeration<String> headerNames = request.getHeaderNames();
                if (headerNames!=null){
                    while (headerNames.hasMoreElements()){
                        String headerName = headerNames.nextElement();
                        String headerValue = request.getHeader(headerName);
                        requestTemplate.header(headerName, headerValue);
                    }
                }
            }
        };
    }

    /**
     * 解决feign异步调用中请求头丢失的问题
     * 原因：异步调用开启新的线程，新线程无法获取当前线程中的请求上下文，RequestContextHolder.getRequestAttributes()为 null
     * 解决：
     *  方案一：使用RequestContextHolder.setRequestAttributes()方法将父线程的Attributes设置到子线程；
     *  方案二：使用如下代码，让DispatcherServlet向子线程传递RequestContext
     */
    //@Bean
    public ServletRegistrationBean<DispatcherServlet> dispatcherRegistration(DispatcherServlet servlet) {
        servlet.setThreadContextInheritable(true);
        return new ServletRegistrationBean<>(servlet, "/**");
    }

}
